package terminal

import (
	"context"
	"encoding/json"
	"strconv"
	"strings"
	"time"

	"device-admin/config/mysql"
	"device-admin/config/redis"
	"device-admin/internal/app/grpc/protoc/robotc"
	"device-admin/internal/dal"
	"device-admin/internal/dao"
	"device-admin/internal/define"
	"device-admin/internal/models"

	"192.168.1.75/go-pkg/go-cache"
	"192.168.1.75/go-pkg/logx"
	_redis "github.com/go-redis/redis/v8"
	"github.com/samber/lo"
)

// RemoveOfflineAlarmCheck 在线处理
func RemoveOfflineAlarmCheck(robotID, terminalType string) {
	// 离线检测仅支持95,97,98
	if !lo.Contains([]string{"95", "97", "98"}, terminalType) {
		return
	}
	rdb := redis.NewDB()
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
	defer cancel()
	// 已经上线的，从离线设备集合中删除
	rdb.ZRem(ctx, define.RedisOfflineDeviceSet, robotID+"-"+terminalType)
}

// AddOfflineAlarmCheck 离线处理
func AddOfflineAlarmCheck(robotID, terminalType string) {
	// 针对98终端，充电时，屏蔽离线告警
	if terminalType == "98" {
		if data, found := cache.HGet(define.CacheRobotRTInfo+robotID, "robot_status"); found {
			if notifyRobotStatus, ok := data.(*robotc.NotifyRobotStatus); ok {
				if notifyRobotStatus.GetBatteryState().GetBcharging() {
					return
				}
			}
		}
	}
	// 离线检测仅支持95,97,98
	if !lo.Contains([]string{"95", "97", "98"}, terminalType) {
		return
	}
	rdb := redis.NewDB()
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
	defer cancel()
	rdb.ZAdd(ctx, define.RedisOfflineDeviceSet, &_redis.Z{
		Score:  float64(time.Now().Unix() + 60*10), // 告警时间为设为10分钟以后
		Member: robotID + "-" + terminalType,
	})
}

type alarmMsgType struct {
	AlarmType int    `json:"alarm_type"` // 告警类型
	RobotID   string `json:"robot_id"`   // 设备ID
	Battery   string `json:"battery"`    // 电量
	CreatedAt int64  `json:"created_at"` // 创建时间
}

// OfflineCheck 离线检测
func OfflineCheck() error {
	rdb := redis.NewDB()
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
	defer cancel()
	minScore := "0"
	maxScore := strconv.FormatInt(time.Now().Unix(), 10)

	// 查询已经10分钟到期的离线设备
	offlineDeviceSet, _ := rdb.ZRevRangeByScore(ctx,
		define.RedisOfflineDeviceSet,
		&_redis.ZRangeBy{
			Min: minScore,
			Max: maxScore,
		}).Result()

	// 删除已经离线告警的设备
	rdb.ZRemRangeByScore(
		context.Background(),
		define.RedisOfflineDeviceSet,
		minScore,
		maxScore,
	)

	// 发送离线告警
	if len(offlineDeviceSet) > 0 {
		for _, offlineDevice := range offlineDeviceSet {
			deviceArr := strings.Split(offlineDevice, "-")
			if len(deviceArr) >= 2 {
				robotID := deviceArr[0]
				terminalType := deviceArr[1]
				terminalAlarmTypeMap := map[string]int{
					"95": 50,
					"97": 51,
					"98": 52,
				}
				var alarmMsg alarmMsgType
				alarmMsg.AlarmType = terminalAlarmTypeMap[terminalType]
				alarmMsg.RobotID = robotID
				alarmMsg.CreatedAt = time.Now().Unix()
				dataBytes, _ := json.Marshal(alarmMsg)
				// 获取开关机配置
				shutdownTimes := []int{}
				records, _ := dao.TerminalConfig{}.GetConfigs(
					context.Background(),
					dal.Use(mysql.NewDB()),
					robotID, "95",
					"auto_boot_shutdown",
				)
				if len(records) > 0 {
					bootShutdownArr := strings.Split(records[0].Value, ",")
					for _, bootShutdownStr := range bootShutdownArr {
						confs := strings.Split(bootShutdownStr, ":")
						if len(confs) == 4 {
							if confs[0] == "1" && confs[1] == "0" {
								h, _ := strconv.Atoi(confs[2])
								m, _ := strconv.Atoi(confs[3])
								shutdownTimes = append(shutdownTimes, h*60+m)
							}
						}
					}
				}
				// 告警推送
				func() {
					ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
					defer cancel()
					// 屏蔽自动关机后的离线关机
					alarmTime := time.Now().Hour()*60 + time.Now().Minute()
					if len(shutdownTimes) > 0 && filterShutdownOfflineAlarm(alarmTime, shutdownTimes) {
						logx.Info(context.Background(), "C2SSS_WILD offline notify disabled", logx.String("robotID", robotID))
					} else {
						redis.NewDB().LPush(ctx, define.RedisAlarmNotifyList, string(dataBytes)).Result()
						logx.Warn(context.Background(), "C2SSS_WILD offline notify", logx.String("robotID", robotID))
					}
				}()
			}
		}
	}
	return nil
}

// PushToActiveDeviceList 添加最近活跃的设备到队列
func PushToActiveDeviceList(robotID, terminalType string) {
	// 当前仅更新95,97和98设备
	if !lo.Contains([]string{"95", "97", "98", "webshell95"}, terminalType) {
		return
	}
	list := define.RedisLatestActiveDeviceList + terminalType
	rdb := redis.NewDB()
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
	defer cancel()
	len, err1 := rdb.LLen(ctx, list).Result()
	if err1 == nil && len < 20000 {
		_, err2 := rdb.LPush(ctx, list, robotID).Result()
		if err2 == nil {
			return // 加入队列成功
		}
	} else {
		logx.Error(context.Background(), "PushToActiveDeviceList error", logx.Int64("length", len), logx.Err(err1))
	}
	// 如果保存到队列失败，直接更新到数据库
	switch terminalType {
	case "95":
		// 更新设备最近活跃时间
		mysql.NewDB().Model(&models.Device{}).
			Where("robot_id = ?", robotID).
			Updates(&models.Device{
				LatestActiveTime: time.Now().Unix(),
			})
		UpdateTerminalActiveTime(robotID, "95", time.Now().Unix())
	default:
		UpdateTerminalActiveTime(robotID, terminalType, time.Now().Unix())
	}
}

// UpdateLatestActiveTime 更新最近活跃时间
func UpdateLatestActiveTime() error {
	// 待处理的redis队列
	lists := []string{
		define.RedisLatestActiveDeviceList + "95",
		define.RedisLatestActiveDeviceList + "97",
		define.RedisLatestActiveDeviceList + "98",
		define.RedisLatestActiveDeviceList + "webshell95",
	}
	rdb := redis.NewDB()
	db := mysql.NewDB()
	for _, list := range lists {
		go func(list string) {
			arr := strings.Split(list, ":")
			if len(arr) < 2 {
				return
			}
			terminalType := arr[len(arr)-1]
			for {
				var (
					robotIDs []string
					redisErr error
				)
				// 一次获取50台设备
				// redis低于6.2.0的版本不支持RPopCount指令，这里做了模拟
				for i := 0; i < 50; i++ {
					var robotID string
					ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
					robotID, redisErr = rdb.RPop(ctx, list).Result()
					cancel()
					if redisErr != nil || robotID == "" {
						break
					}
					robotIDs = append(robotIDs, robotID)
				}
				// 更新或退出
				if len(robotIDs) > 0 {
					// 95设备表的处理
					if terminalType == "95" {
						db.Model(&models.Device{}).
							Where("robot_id IN ?", lo.Uniq(robotIDs)).
							Update("latest_active_time", time.Now().Unix())
					}
					// 更新对应终端类型的最近活跃时间
					db.Model(&models.Terminal{}).
						Where("robot_id IN ?", lo.Uniq(robotIDs)).
						Where("terminal_type = ?", terminalType).
						Update("latest_active_time", time.Now().Unix())
				} else {
					break
				}
			}
		}(list)
	}
	return nil
}

// filterShutdownOfflineAlarm 过滤掉关机的离线告警
// alarmTime int64 触发告警的时间
// shutdownTimes []string 关机时间组
func filterShutdownOfflineAlarm(alarmTime int, shutdownTimes []int) bool {
	for _, shutdownTime := range shutdownTimes {
		if shutdownTime > alarmTime { // 凌晨临界点情况
			alarmTime = alarmTime + 24*60
		}
		if alarmTime >= shutdownTime && alarmTime <= shutdownTime+12 {
			return true
		}
	}
	return false
}
